/*******************************************************************************
 * Copyright (c) 2009 Luaj.org. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 ******************************************************************************/
package org.luaj.vm2;

/**
 * Class to encapsulate varargs values, either as part of a variable argument list, or multiple return values.
 * <p>
 * To construct varargs, use one of the static methods such as
 * {@code LuaValue.varargsOf(LuaValue,LuaValue)}
 * <p>
 * <p>
 * Any LuaValue can be used as a stand-in for Varargs, for both calls and return values.
 * When doing so, nargs() will return 1 and arg1() or arg(1) will return this.
 * This simplifies the case when calling or implementing varargs functions with only
 * 1 argument or 1 return value.
 * <p>
 * Varargs can also be derived from other varargs by appending to the front with a call
 * such as  {@code LuaValue.varargsOf(LuaValue,Varargs)}
 * or by taking a portion of the args using {@code Varargs.subargs(int start)}
 * <p>
 *
 * @see LuaValue#varargsOf(LuaValue[])
 * @see LuaValue#varargsOf(LuaValue, Varargs)
 * @see LuaValue#varargsOf(LuaValue[], Varargs)
 * @see LuaValue#varargsOf(LuaValue, LuaValue, Varargs)
 * @see LuaValue#varargsOf(LuaValue[], int, int)
 * @see LuaValue#varargsOf(LuaValue[], int, int, Varargs)
 * @see LuaValue#subargs(int)
 */
public abstract class Varargs {

    /**
     * Get the n-th argument value (1-based).
     *
     * @param i the index of the argument to get, 1 is the first argument
     * @return Value at position i, or LuaValue.NIL if there is none.
     * @see Varargs#arg1()
     * @see LuaValue#NIL
     */
    abstract public LuaValue arg(int i);

    /**
     * Get the number of arguments, or 0 if there are none.
     *
     * @return number of arguments.
     */
    abstract public int narg();

    /**
     * Get the first argument in the list.
     *
     * @return LuaValue which is first in the list, or LuaValue.NIL if there are no values.
     * @see Varargs#arg(int)
     * @see LuaValue#NIL
     */
    abstract public LuaValue arg1();

    /**
     * Evaluate any pending tail call and return result.
     *
     * @return the evaluated tail call result
     */
    public Varargs eval() {
        return this;
    }

    /**
     * Return true if this is a TailcallVarargs
     *
     * @return true if a tail call, false otherwise
     */
    public boolean isTailcall() {
        return false;
    }

    // -----------------------------------------------------------------------
    // utilities to get specific arguments and type-check them.
    // -----------------------------------------------------------------------

    /**
     * Gets the type of argument {@code i}
     *
     * @param i the index of the argument to convert, 1 is the first argument
     * @return int value corresponding to one of the LuaValue integer type values

     */
    public int type(int i) {
        return arg(i).type();
    }

    /**
     * Tests if argument i is nil.
     *
     * @param i the index of the argument to test, 1 is the first argument
     * @return true if the argument is nil or does not exist, false otherwise
     */
    public boolean isnil(int i) {
        return arg(i).isnil();
    }

    /**
     * Tests if argument i is a function.
     *
     * @param i the index of the argument to test, 1 is the first argument
     * @return true if the argument exists and is a function or closure, false otherwise
     */
    public boolean isfunction(int i) {
        return arg(i).isfunction();
    }

    /**
     * Tests if argument i is a number.
     * Since anywhere a number is required, a string can be used that
     * is a number, this will return true for both numbers and
     * strings that can be interpreted as numbers.
     *
     * @param i the index of the argument to test, 1 is the first argument
     * @return true if the argument exists and is a number or
     * string that can be interpreted as a number, false otherwise
     */
    public boolean isnumber(int i) {
        return arg(i).isnumber();
    }

    /**
     * Tests if argument i is a string.
     * Since all lua numbers can be used where strings are used,
     * this will return true for both strings and numbers.
     *
     * @param i the index of the argument to test, 1 is the first argument
     * @return true if the argument exists and is a string or number, false otherwise
     */
    public boolean isstring(int i) {
        return arg(i).isstring();
    }

    /**
     * Tests if argument i is a table.
     *
     * @param i the index of the argument to test, 1 is the first argument
     * @return true if the argument exists and is a lua table, false otherwise
     */
    public boolean istable(int i) {
        return arg(i).istable();
    }

    /**
     * Tests if argument i is a thread.
     *
     * @param i the index of the argument to test, 1 is the first argument
     * @return true if the argument exists and is a lua thread, false otherwise
     */
    public boolean isthread(int i) {
        return arg(i).isthread();
    }

    /**
     * Tests if argument i is a userdata.
     *
     * @param i the index of the argument to test, 1 is the first argument
     * @return true if the argument exists and is a userdata, false otherwise
     */
    public boolean isuserdata(int i) {
        return arg(i).isuserdata();
    }


//	public boolean isvalue(int i)      { return i>0 && i<=narg(); } remove by yanqiu

    /**
     * Return argument i as a boolean value, {@code defval} if nil, or throw a LuaError if any other type.
     *
     * @param i the index of the argument to test, 1 is the first argument
     * @return true if argument i is boolean true, false if it is false, or defval if not supplied or nil
     * @throws LuaError if the argument is not a lua boolean
     */
    public boolean optboolean(int i, boolean defval) {
        return arg(i).optboolean(defval);
    }


//	public LuaClosure   optclosure(int i, LuaClosure defval)       { return arg(i).optclosure(defval); } remove by yanqiu

    /**
     * Return argument i as a double, {@code defval} if nil, or throw a LuaError if it cannot be converted to one.
     *
     * @param i the index of the argument to test, 1 is the first argument
     * @return java double value if argument i is a number or string that converts to a number, or defval if not supplied or nil
     * @throws LuaError if the argument is not a number
     */
    public double optdouble(int i, double defval) {
        return arg(i).optdouble(defval);
    }

    /**
     * Return argument i as a function, {@code defval} if nil, or throw a LuaError  if an incompatible type.
     *
     * @param i the index of the argument to test, 1 is the first argument
     * @return LuaValue that can be called if argument i is lua function or closure, or defval if not supplied or nil
     * @throws LuaError if the argument is not a lua function or closure
     */
    public LuaFunction optfunction(int i, LuaFunction defval) {
        return arg(i).optfunction(defval);
    }

    /**
     * Return argument i as a java int value, discarding any fractional part, {@code defval} if nil, or throw a LuaError  if not a number.
     *
     * @param i the index of the argument to test, 1 is the first argument
     * @return int value with fraction discarded and truncated if necessary if argument i is number, or defval if not supplied or nil
     * @throws LuaError if the argument is not a number
     */
    public int optint(int i, int defval) {
        return arg(i).optint(defval);
    }

    public LuaInteger optinteger(int i, LuaInteger defval) {
        return arg(i).optinteger(defval);
    }

    /**
     * Return argument i as a java long value, discarding any fractional part, {@code defval} if nil, or throw a LuaError  if not a number.
     *
     * @param i the index of the argument to test, 1 is the first argument
     * @return long value with fraction discarded and truncated if necessary if argument i is number, or defval if not supplied or nil
     * @throws LuaError if the argument is not a number
     */
    public long optlong(int i, long defval) {
        return arg(i).optlong(defval);
    }

    /**
     * Return argument i as a LuaNumber, {@code defval} if nil, or throw a LuaError  if not a number or string that can be converted to a number.
     *
     * @param i the index of the argument to test, 1 is the first argument, or defval if not supplied or nil
     * @return LuaNumber if argument i is number or can be converted to a number
     * @throws LuaError if the argument is not a number
     */
    public LuaNumber optnumber(int i, LuaNumber defval) {
        return arg(i).optnumber(defval);
    }

    /**
     * Return argument i as a java String if a string or number, {@code defval} if nil, or throw a LuaError  if any other type
     *
     * @param i the index of the argument to test, 1 is the first argument
     * @return String value if argument i is a string or number, or defval if not supplied or nil
     * @throws LuaError if the argument is not a string or number
     */
    public String optjstring(int i, String defval) {
        return arg(i).optjstring(defval);
    }

    /**
     * Return argument i as a LuaString if a string or number, {@code defval} if nil, or throw a LuaError  if any other type
     *
     * @param i the index of the argument to test, 1 is the first argument
     * @return LuaString value if argument i is a string or number, or defval if not supplied or nil
     * @throws LuaError if the argument is not a string or number
     */
    public LuaString optstring(int i, LuaString defval) {
        return arg(i).optstring(defval);
    }

    /**
     * Return argument i as a LuaTable if a lua table, {@code defval} if nil, or throw a LuaError  if any other type.
     *
     * @param i the index of the argument to test, 1 is the first argument
     * @return LuaTable value if a table, or defval if not supplied or nil
     * @throws LuaError if the argument is not a lua table
     */
    public LuaTable opttable(int i, LuaTable defval) {
        return arg(i).opttable(defval);
    }

    /**
     * Return argument i as a LuaThread if a lua thread, {@code defval} if nil, or throw a LuaError  if any other type.
     *
     * @param i the index of the argument to test, 1 is the first argument
     * @return LuaThread value if a thread, or defval if not supplied or nil
     * @throws LuaError if the argument is not a lua thread
     */
    public LuaThread optthread(int i, LuaThread defval) {
        return arg(i).optthread(defval);
    }

    /**
     * Return argument i as a java Object if a userdata, {@code defval} if nil, or throw a LuaError  if any other type.
     *
     * @param i the index of the argument to test, 1 is the first argument
     * @return java Object value if argument i is a userdata, or defval if not supplied or nil
     * @throws LuaError if the argument is not a userdata
     */
    public Object optuserdata(int i, Object defval) {
        return arg(i).optuserdata(defval);
    }

    /**
     * Return argument i as a java Object if it is a userdata whose instance Class c or a subclass,
     * {@code defval} if nil, or throw a LuaError  if any other type.
     *
     * @param i the index of the argument to test, 1 is the first argument
     * @param c the class to which the userdata instance must be assignable
     * @return java Object value if argument i is a userdata whose instance Class c or a subclass, or defval if not supplied or nil
     * @throws LuaError if the argument is not a userdata or from whose instance c is not assignable
     */
    public Object optuserdata(int i, Class c, Object defval) {
        return arg(i).optuserdata(c, defval);
    }

    /**
     * Return argument i as a LuaValue if it exists, or {@code defval}.
     *
     * @param i the index of the argument to test, 1 is the first argument
     * @return LuaValue value if the argument exists, defval if not
     * @throws LuaError if the argument does not exist.
     */
    public LuaValue optvalue(int i, LuaValue defval) {
        return i > 0 && i <= narg() ? arg(i) : defval;
    }

    /**
     * Return argument i as a boolean value, or throw an error if any other type.
     *
     * @param i the index of the argument to test, 1 is the first argument
     * @return true if argument i is boolean true, false if it is false
     * @throws LuaError if the argument is not a lua boolean
     */
    public boolean checkboolean(int i) {
        return arg(i).checkboolean();
    }

    /**
     * Return argument i as a closure, or throw an error if any other type.
     *
     * @param i the index of the argument to test, 1 is the first argument
     * @return LuaClosure if argument i is a closure.
     * @throws LuaError if the argument is not a lua closure
     */
    public LuaClosure checkclosure(int i) {
        return arg(i).checkclosure();
    }

    /**
     * Return argument i as a double, or throw an error if it cannot be converted to one.
     *
     * @param i the index of the argument to test, 1 is the first argument
     * @return java double value if argument i is a number or string that converts to a number
     * @throws LuaError if the argument is not a number
     */
    public double checkdouble(int i) {
        return arg(i).checknumber().todouble();
    }

    /**
     * Return argument i as a function, or throw an error if an incompatible type.
     *
     * @param i the index of the argument to test, 1 is the first argument
     * @return LuaValue that can be called if argument i is lua function or closure
     * @throws LuaError if the argument is not a lua function or closure
     */
    public LuaFunction checkfunction(int i) {
        return arg(i).checkfunction();
    }

    /**
     * Return argument i as a java int value, discarding any fractional part, or throw an error if not a number.
     *
     * @param i the index of the argument to test, 1 is the first argument
     * @return int value with fraction discarded and truncated if necessary if argument i is number
     * @throws LuaError if the argument is not a number
     */
    public int checkint(int i) {
        return arg(i).checknumber().toint();
    }

    /**
     * Return argument i as a java int value, or throw an error if not a number or is not representable by a java int.
     *
     * @param i the index of the argument to test, 1 is the first argument
     * @return LuaInteger value that fits in a java int without rounding
     * @throws LuaError if the argument cannot be represented by a java int value
     */
    public LuaInteger checkinteger(int i) {
        return arg(i).checkinteger();
    }

    /**
     * Return argument i as a java long value, discarding any fractional part, or throw an error if not a number.
     *
     * @param i the index of the argument to test, 1 is the first argument
     * @return long value with fraction discarded and truncated if necessary if argument i is number
     * @throws LuaError if the argument is not a number
     */
    public long checklong(int i) {
        return arg(i).checknumber().tolong();
    }

    /**
     * Return argument i as a LuaNumber, or throw an error if not a number or string that can be converted to a number.
     *
     * @param i the index of the argument to test, 1 is the first argument
     * @return LuaNumber if argument i is number or can be converted to a number
     * @throws LuaError if the argument is not a number
     */
    public LuaNumber checknumber(int i) {
        return arg(i).checknumber();
    }

    /**
     * Return argument i as a java String if a string or number, or throw an error if any other type
     *
     * @param i the index of the argument to test, 1 is the first argument
     * @return String value if argument i is a string or number
     * @throws LuaError if the argument is not a string or number
     */
    public String checkjstring(int i) {
        return arg(i).checkjstring();
    }

    /**
     * Return argument i as a LuaString if a string or number, or throw an error if any other type
     *
     * @param i the index of the argument to test, 1 is the first argument
     * @return LuaString value if argument i is a string or number
     * @throws LuaError if the argument is not a string or number
     */
    public LuaString checkstring(int i) {
        return arg(i).checkstring();
    }

    /**
     * Return argument i as a LuaTable if a lua table, or throw an error if any other type.
     *
     * @param i the index of the argument to test, 1 is the first argument
     * @return LuaTable value if a table
     * @throws LuaError if the argument is not a lua table
     */
    public LuaTable checktable(int i) {
        return arg(i).checktable();
    }

    /**
     * Return argument i as a LuaThread if a lua thread, or throw an error if any other type.
     *
     * @param i the index of the argument to test, 1 is the first argument
     * @return LuaThread value if a thread
     * @throws LuaError if the argument is not a lua thread
     */
    public LuaThread checkthread(int i) {
        return arg(i).checkthread();
    }

    /**
     * Return argument i as a java Object if a userdata, or throw an error if any other type.
     *
     * @param i the index of the argument to test, 1 is the first argument
     * @return java Object value if argument i is a userdata
     * @throws LuaError if the argument is not a userdata
     */
    public Object checkuserdata(int i) {
        return arg(i).checkuserdata();
    }

    /**
     * Return argument i as a java Object if it is a userdata whose instance Class c or a subclass,
     * or throw an error if any other type.
     *
     * @param i the index of the argument to test, 1 is the first argument
     * @param c the class to which the userdata instance must be assignable
     * @return java Object value if argument i is a userdata whose instance Class c or a subclass
     * @throws LuaError if the argument is not a userdata or from whose instance c is not assignable
     */
    public Object checkuserdata(int i, Class c) {
        return arg(i).checkuserdata(c);
    }

    /**
     * Return argument i as a LuaValue if it exists, or throw an error.
     *
     * @param i the index of the argument to test, 1 is the first argument
     * @return LuaValue value if the argument exists
     * @throws LuaError if the argument does not exist.
     */
    public LuaValue checkvalue(int i) {
        return i <= narg() ? arg(i) : LuaValue.argerror(i, "value expected");
    }

    /**
     * Return argument i as a LuaValue if it is not nil, or throw an error if it is nil.
     *
     * @param i the index of the argument to test, 1 is the first argument
     * @return LuaValue value if the argument is not nil
     * @throws LuaError if the argument doesn't exist or evaluates to nil.
     */
    public LuaValue checknotnil(int i) {
        return arg(i).checknotnil();
    }

    /**
     * Return argument i as a LuaValue when a user-supplied assertion passes, or throw an error.
     *
     * @param test user supplied assertion to test against
     * @param i    the index to report in any error message
     * @param msg  the error message to use when the test fails
     * @return LuaValue value if the value of {@code test} is {@code true}
     * @throws LuaError if the the value of {@code test} is {@code false}
     */
    public void argcheck(boolean test, int i, String msg) {
        if (!test) LuaValue.argerror(i, msg);
    }

    /**
     * Return true if there is no argument or nil at argument i.
     *
     * @param i the index of the argument to test, 1 is the first argument
     * @return true if argument i contains either no argument or nil
     */
    public boolean isnoneornil(int i) {
        return i > narg() || arg(i).isnil();
    }

    /**
     * Convert argument {@code i} to java boolean based on lua rules for boolean evaluation.
     *
     * @param i the index of the argument to convert, 1 is the first argument
     * @return {@code false} if argument i is nil or false, otherwise {@code true}
     */
    public boolean toboolean(int i) {
        return arg(i).toboolean();
    }

    /**
     * Return argument i as a java byte value, discarding any fractional part and truncating,
     * or 0 if not a number.
     *
     * @param i the index of the argument to convert, 1 is the first argument
     * @return byte value with fraction discarded and truncated if necessary if argument i is number, otherwise 0
     */
    public byte tobyte(int i) {
        return arg(i).tobyte();
    }

    /**
     * Return argument i as a java char value, discarding any fractional part and truncating,
     * or 0 if not a number.
     *
     * @param i the index of the argument to convert, 1 is the first argument
     * @return char value with fraction discarded and truncated if necessary if argument i is number, otherwise 0
     */
    public char tochar(int i) {
        return arg(i).tochar();
    }

    /**
     * Return argument i as a java double value or 0 if not a number.
     *
     * @param i the index of the argument to convert, 1 is the first argument
     * @return double value if argument i is number, otherwise 0
     */
    public double todouble(int i) {
        return arg(i).todouble();
    }

    /**
     * Return argument i as a java float value, discarding excess fractional part and truncating,
     * or 0 if not a number.
     *
     * @param i the index of the argument to convert, 1 is the first argument
     * @return float value with excess fraction discarded and truncated if necessary if argument i is number, otherwise 0
     */
    public float tofloat(int i) {
        return arg(i).tofloat();
    }

    /**
     * Return argument i as a java int value, discarding any fractional part and truncating,
     * or 0 if not a number.
     *
     * @param i the index of the argument to convert, 1 is the first argument
     * @return int value with fraction discarded and truncated if necessary if argument i is number, otherwise 0
     */
    public int toint(int i) {
        return arg(i).toint();
    }

    /**
     * Return argument i as a java long value, discarding any fractional part and truncating,
     * or 0 if not a number.
     *
     * @param i the index of the argument to convert, 1 is the first argument
     * @return long value with fraction discarded and truncated if necessary if argument i is number, otherwise 0
     */
    public long tolong(int i) {
        return arg(i).tolong();
    }

    /**
     * Return argument i as a java String based on the type of the argument.
     *
     * @param i the index of the argument to convert, 1 is the first argument
     * @return String value representing the type
     */
    public String tojstring(int i) {
        return arg(i).tojstring();
    }

    /**
     * Return argument i as a java short value, discarding any fractional part and truncating,
     * or 0 if not a number.
     *
     * @param i the index of the argument to convert, 1 is the first argument
     * @return short value with fraction discarded and truncated if necessary if argument i is number, otherwise 0
     */
    public short toshort(int i) {
        return arg(i).toshort();
    }

    /**
     * Return argument i as a java Object if a userdata, or null.
     *
     * @param i the index of the argument to convert, 1 is the first argument
     * @return java Object value if argument i is a userdata, otherwise null
     */
    public Object touserdata(int i) {
        return arg(i).touserdata();
    }

    /**
     * Return argument i as a java Object if it is a userdata whose instance Class c or a subclass, or null.
     *
     * @param i the index of the argument to convert, 1 is the first argument
     * @param c the class to which the userdata instance must be assignable
     * @return java Object value if argument i is a userdata whose instance Class c or a subclass, otherwise null
     */
    public Object touserdata(int i, Class c) {
        return arg(i).touserdata(c);
    }

    /**
     * Convert the list of varargs values to a human readable java String.
     *
     * @return String value in human readable form such as {1,2}.
     */
    public String tojstring() {
        Buffer sb = new Buffer();
        sb.append("(");
        for (int i = 1, n = narg(); i <= n; i++) {
            if (i > 1) sb.append(",");
            sb.append(arg(i).tojstring());
        }
        sb.append(")");
        return sb.tojstring();
    }

    /**
     * Convert the value or values to a java String using Varargs.tojstring()
     *
     * @return String value in human readable form.
     * @see Varargs#tojstring()
     */
    public String toString() {
        return tojstring();
    }

    /**
     * Create a {@code Varargs} instance containing arguments starting at index {@code start}
     *
     * @param start the index from which to include arguments, where 1 is the first argument.
     * @return Varargs containing argument { start, start+1,  ... , narg-start-1 }
     */
    abstract public Varargs subargs( int start);

    /**
     * Implementation of Varargs for use in the Varargs.subargs() function.
     *
     * @see Varargs#subargs(int)
     */
//    static class SubVarargs extends Varargs {
//        private  Varargs v;
//        private  int start;
//        private  int end;
//
//        public SubVarargs(Varargs varargs, int start, int end) {
//            this.v = varargs;
//            this.start = start;
//            this.end = end;
//        }
//
//        public LuaValue arg(int i) {
//            i += start - 1;
//            return i >= start && i <= end ? v.arg(i) : LuaValue.NIL;
//        }
//
//        public LuaValue arg1() {
//            return v.arg(start);
//        }
//
//        public int narg() {
//            return end + 1 - start;
//        }
//
//        public Varargs subargs( int start) {
//            if (start == 1)
//                return this;
//             int newstart = this.start + start - 1;
//            if (start > 0) {
//                if (newstart >= this.end)
//                    return LuaValue.NONE;
//                if (newstart == this.end)
//                    return v.arg(this.end);
//                if (newstart == this.end - 1)
//                    return new Varargs.PairVarargs(v.arg(this.end - 1), v.arg(this.end));
//                return new SubVarargs(v, newstart, this.end);
//            }
//            return new SubVarargs(v, newstart, this.end);
//        }
//    }

    /**
     * Varargs implemenation backed by two values.
     * <p>
     * This is an internal class not intended to be used directly.
     * Instead use the corresponding static method on LuaValue.
     *
     * @see LuaValue#varargsOf(LuaValue, Varargs)
     */
    static  class PairVarargs extends Varargs {
        private  LuaValue v1;
        private  Varargs v2;

        /**
         * Construct a Varargs from an two LuaValue.
         * <p>
         * This is an internal class not intended to be used directly.
         * Instead use the corresponding static method on LuaValue.
         *
         * @see LuaValue#varargsOf(LuaValue, Varargs)
         */
        PairVarargs(LuaValue v1, Varargs v2) {
            this.v1 = v1;
            this.v2 = v2;
        }

        public LuaValue arg(int i) {
            return i == 1 ? v1 : v2.arg(i - 1);
        }

        public int narg() {
            return 1 + v2.narg();
        }

        public LuaValue arg1() {
            return v1;
        }

        public Varargs subargs( int start) {
            if (start == 1)
                return this;
            if (start == 2)
                return v2;
            if (start > 2)
                return v2.subargs(start - 1);
            return LuaValue.argerror(1, "start must be > 0");
        }
    }

    /**
     * Varargs implemenation backed by an array of LuaValues
     * <p>
     * This is an internal class not intended to be used directly.
     * Instead use the corresponding static methods on LuaValue.
     *
     * @see LuaValue#varargsOf(LuaValue[])
     * @see LuaValue#varargsOf(LuaValue[], Varargs)
     */
    static  class ArrayVarargs extends Varargs {
        private  LuaValue[] v;
        private  Varargs r;

        /**
         * Construct a Varargs from an array of LuaValue.
         * <p>
         * This is an internal class not intended to be used directly.
         * Instead use the corresponding static methods on LuaValue.
         *
         * @see LuaValue#varargsOf(LuaValue[])
         * @see LuaValue#varargsOf(LuaValue[], Varargs)
         */
        ArrayVarargs(LuaValue[] v, Varargs r) {
            this.v = v;
            this.r = r;
            for (int i = 0; i < v.length; ++i)
                if (v[i] == null)
                    throw new IllegalArgumentException("nulls in array");
        }

        public LuaValue arg(int i) {
            return i < 1 ? LuaValue.NIL : i <= v.length ? v[i - 1] : r.arg(i - v.length);
        }

        public int narg() {
            return v.length + r.narg();
        }

        public LuaValue arg1() {
            return v.length > 0 ? v[0] : r.arg1();
        }

        public Varargs subargs(int start) {
            if (start <= 0)
                LuaValue.argerror(1, "start must be > 0");
            if (start == 1)
                return this;
            if (start > v.length)
                return r.subargs(start - v.length);
            return LuaValue.varargsOf(v, start - 1, v.length - (start - 1), r);
        }
    }

    /**
     * Varargs implemenation backed by an array of LuaValues
     * <p>
     * This is an internal class not intended to be used directly.
     * Instead use the corresponding static methods on LuaValue.
     *
     * @see LuaValue#varargsOf(LuaValue[], int, int)
     * @see LuaValue#varargsOf(LuaValue[], int, int, Varargs)
     */
    static  class ArrayPartVarargs extends Varargs {
        private  int offset;
        private  LuaValue[] v;
        private  int length;
        private  Varargs more;

        /**
         * Construct a Varargs from an array of LuaValue.
         * <p>
         * This is an internal class not intended to be used directly.
         * Instead use the corresponding static methods on LuaValue.
         *
         * @see LuaValue#varargsOf(LuaValue[], int, int)
         */
        ArrayPartVarargs(LuaValue[] v, int offset, int length) {
            this.v = v;
            this.offset = offset;
            this.length = length;
            this.more = LuaValue.NONE;
        }

        /**
         * Construct a Varargs from an array of LuaValue and additional arguments.
         * <p>
         * This is an internal class not intended to be used directly.
         * Instead use the corresponding static method on LuaValue.
         *
         * @see LuaValue#varargsOf(LuaValue[], int, int, Varargs)
         */
        public ArrayPartVarargs(LuaValue[] v, int offset, int length, Varargs more) {
            this.v = v;
            this.offset = offset;
            this.length = length;
            this.more = more;
        }

        public LuaValue arg( int i) {
            return i < 1 ? LuaValue.NIL : i <= length ? v[offset + i - 1] : more.arg(i - length);
        }

        public int narg() {
            return length + more.narg();
        }

        public LuaValue arg1() {
            return length > 0 ? v[offset] : more.arg1();
        }

        public Varargs subargs(int start) {
            if (start <= 0)
                LuaValue.argerror(1, "start must be > 0");
            if (start == 1)
                return this;
            if (start > length)
                return more.subargs(start - length);
            return LuaValue.varargsOf(v, offset + start - 1, length - (start - 1), more);
        }
    }
}
